home *** CD-ROM | disk | FTP | other *** search
- /*
- * gc.c
- * The garbage collector.
- *
- * Copyright (c) 1996 Systems Architecture Research Centre,
- * City University, London, UK.
- *
- * See the file "license.terms" for information on usage and redistribution
- * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
- *
- * Written by Tim Wilkinson <tim@sarc.city.ac.uk>, February 1996.
- */
-
- /* #define NOGC /* Turn off garbage collection */
-
- #define DBG(s)
- #define MDBG(s)
- #define FDBG(s)
- #define ADBG(s)
-
- #include <assert.h>
- #include "gtypes.h"
- #include "object.h"
- #include "classMethod.h"
- #include "baseClasses.h"
- #include "lookup.h"
- #include "thread.h"
- #include "gc.h"
- #include "md.h"
-
- extern thread* threadQhead[];
- extern classes* ThreadClass;
- extern strpair* finalpair;
- extern thread* finalman;
- extern thread* gcman;
-
- static gcRef* refTable[GCREFTABLESZ];
- static int refTableIdx;
- static gcRef* freeRef;
- static gcRef* permList;
- static void* minGcMemory = (void*)-1;
- static void* maxGcMemory = (void*)0;
- static gcRef* garbageObjects;
- static unsigned int allocCount;
-
- /* When's a good time to run the GC? Run it every 1000 allocations */
- #define GCTRIGCOUNT 1000
-
- static void markObject(object*);
- static void garbageCollect(void);
- static gcRef* validReference(object*);
-
- /*
- * Add an object into the garbage collection scheme.
- */
- void
- add_object(object* o, bool perm)
- {
- gcRef* table;
- gcRef* ref;
- int i;
-
- #if defined(NOGC)
- return;
- #endif
-
- /* Reset min and max memory pointers */
- if ((void*)o < minGcMemory) {
- minGcMemory = (void*)o;
- }
- if ((void*)o > maxGcMemory) {
- maxGcMemory = (void*)o;
- }
-
- ref = freeRef;
- if (ref != 0) {
- freeRef = freeRef->next;
- }
- else {
- assert(refTableIdx < GCREFTABLESZ);
- table = (gcRef*)malloc(sizeof(gcRef) * GCREFMAX);
- assert(table != 0);
-
- /* Build into free list */
- for (i = 0; i < GCREFMAX; i++) {
- table[i].flags = GC_FREE;
- table[i].next = &table[i+1];
- table[i].idx = i + (refTableIdx * GCREFMAX);
- }
- table[GCREFMAX-1].next = 0;
- freeRef = &table[1];
- ref = &table[0];
-
- refTable[refTableIdx] = table;
- refTableIdx++;
- }
-
- assert(ref->flags == GC_FREE);
-
- /* Make ref point at object, and object point at ref */
- o->idx = ref->idx;
- ref->obj = o;
- ref->flags = GC_UNMARK;
- ref->next = 0;
-
- /* If object is permenant, put it on the perm list */
- if (perm == true) {
- ref->next = permList;
- permList = ref;
- }
-
- ADBG( printf("Adding new object 0x%x at index %d - %s\n", o, o->idx, o->type ? o->type->name : "<none>"); )
-
- if (++allocCount % GCTRIGCOUNT == 0) {
- invokeGarbageCollector();
- }
- }
-
- /*
- * Invoke the garbage collector (if we have one)
- */
- void
- invokeGarbageCollector(void)
- {
- if (gcman != 0) {
- lockMutex(&gcman->obj);
- signalCond(&gcman->obj);
- unlockMutex(&gcman->obj);
- }
- }
-
- /*
- * Is object reference a real object?
- */
- static
- inline
- gcRef*
- validReference(object* o)
- {
- gcRef* ref;
-
- /* Validate object address. To be a real object it must lie
- inside the malloced memory pool, and its index must refer to
- a GC entry which refers to it. */
- if ((void*)o < minGcMemory || (void*)o > maxGcMemory) {
- return (0);
- }
- ref = refTable[(o->idx / GCREFMAX) % GCREFTABLESZ];
- if (ref == 0) {
- return (0);
- }
- ref = &ref[o->idx % GCREFMAX];
- if (ref->obj != o) {
- return (0);
- }
-
- /* Stop when we reach a mark */
- if (ref->flags != GC_UNMARK) {
- return (0);
- }
-
- return (ref);
- }
-
- /*
- * Garbage collect objects.
- */
- static
- void
- garbageCollect(void)
- {
- int i;
- thread* tid;
- object** ptr;
- gcRef* ref;
- int j;
- gcRef* table;
-
- #if defined(NOGC)
- return;
- #endif
-
- DBG( printf("Garbage collecting ... \n"); )
-
- /* Scan the permentant object list */
- for (ref = permList; ref != 0; ref = ref->next) {
- markObject(ref->obj);
- }
-
- /* Scan each thread */
- for (i = MIN_THREAD_PRIO; i <= MAX_THREAD_PRIO; i++) {
- for (tid = threadQhead[i]; tid != 0; tid = tid->next) {
- markObject(&tid->obj);
- for (ptr = (object**)tid->eetop->restorePoint; ptr < (object**)tid->eetop->stackEnd; ptr++) {
- markObject(*ptr);
- }
- }
- }
-
- /* Now look through object table for objects which have not
- been marked. These can be freed. */
- for (j = 0; j < refTableIdx; j++) {
- table = refTable[j];
- for (i = 0; i < GCREFMAX; i++) {
- switch(table[i].flags) {
- case GC_GARBAGE:
- case GC_FREE:
- break;
-
- case GC_MARK:
- table[i].flags = GC_UNMARK; /* For next time */
- break;
-
- case GC_UNMARK:
- /* Object not marked - free */
- table[i].flags = GC_GARBAGE;
- table[i].next = garbageObjects;
- garbageObjects = &table[i];
- break;
- }
- }
- }
-
- DBG( printf("Garbage collecting ... done.\n"); )
- }
-
- /*
- * Given an object, mark it and follow any object references it contains.
- */
- static
- void
- markObject(object* o)
- {
- object** child;
- classes* class;
- fields* fld;
- int i;
- gcRef* ref;
-
- /* Null object reference */
- if (o == 0) {
- return;
- }
-
- /* Invalid object or already visited? */
- ref = validReference(o);
- if (ref == 0) {
- return;
- }
-
- MDBG( printf("Marking object 0x%x at index %d - %s\n", o, o->idx, o->type ? o->type->name : "<none>"); )
-
- /* Mark this object */
- ref->flags = GC_MARK;
-
- class = o->type;
-
- /* Mark the class object */
- markObject(&class->head);
-
- /* If this is a class object, mark the static fields and constants */
- if (class == ClassClass) {
- class = (classes*)o;
- markObject(&class->superclass->head);
- child = (object**)class->staticFields;
- for (i = 0; i < class->sfsize; i++) {
- markObject(child[i]);
- }
- if (class->constants != 0) {
- child = (object**)class->constants->data;
- for (i = class->constants->size - 1; i >= 0; i--) {
- markObject(child[i]);
- }
- }
- }
- /* Otherwise, a normal object or an array of objects then ... */
- else if (o->type->sig[0] == 'L' || o->type->sig[1] == 'L') {
- assert(o->type->sig[0] == 'L' || o->type->sig[0] == '[');
- child = (object**)o->data;
- for (i = 0; i < o->size; i++) {
- markObject(child[i]);
- }
- }
- }
-
- /*
- * Finaliser.
- * Finalises any objects which have been garbage collected before
- * deleting them.
- */
- void
- finaliserMan(void)
- {
- object* obj;
- gcRef* ref;
- void* func;
-
- /* All threads start with interrupts disabled */
- intsRestore();
-
- lockMutex(¤tThread->obj);
- for (;;) {
- while (garbageObjects) {
- ref = garbageObjects;
- garbageObjects = garbageObjects->next;
- assert(ref->flags == GC_GARBAGE);
- ref->next = 0;
- obj = ref->obj;
-
- /* Finalise object if necessary */
- if (obj->final == 0) {
- obj->final = 1;
- func = findMethod(obj->type, finalpair);
- assert(func != 0);
- CALL_KAFFE_FUNCTION_VARARGS(func, obj, 0, 0);
- /* Don't free it cause it might live */
- ref->flags = GC_UNMARK;
- continue;
- }
- /* Special handling for threads */
- if (obj->type == ThreadClass) {
- thread* stiff = (thread*)obj;
- free(stiff->eetop->stackBase);
- free(stiff->eetop);
- }
- /* Don't handle classes yet */
- else if (obj->type == ClassClass) {
- abort();
- }
-
- /* Put entry onto freelist */
- ref->flags = GC_FREE;
- ref->next = freeRef;
- freeRef = ref;
-
- /* Free the object */
- FDBG( printf("Freeing object 0x%x(%d) %s\n", obj,
- ref->idx, obj->type->name); )
- free(obj);
- }
- waitCond(¤tThread->obj, -1);
- }
- }
-
- /*
- * Garbage collector.
- * Run the garbage collector.
- */
- void
- gcMan(void)
- {
- /* All threads start with interrupts disabled */
- intsRestore();
-
- lockMutex(¤tThread->obj);
- for (;;) {
- waitCond(¤tThread->obj, -1);
-
- /* Run the garbage collector */
- garbageCollect();
-
- /* If there's garbage, finalise it */
- if (garbageObjects != 0 && finalman != 0) {
- lockMutex(&finalman->obj);
- signalCond(&finalman->obj);
- unlockMutex(&finalman->obj);
- }
- }
- }
-